import configparser, os
import time
import paho.mqtt.client as mqtt
import configparser
import logging
from threading import Thread
from queue import Queue
import binascii
import re
import json

import platform

"""日志文件，调试用mqtt.log"""
logging.basicConfig(filename=os.getcwd()+'/conf/mqtt.log',
                    format='%(asctime)s - %(levelname)s - %(message)s',
                    level=logging.DEBUG)
logging.info('Run...')

"""配置文件读取"""
config = configparser.ConfigParser()
config_file = os.getcwd() +'/conf/mqtt_test.conf'
config.read(config_file)

# mqtt配置
MQTT_HOST = config['mqtt']['host']
MQTT_PORT = int(config['mqtt']['port'])
MQTT_ADMIN = config['mqtt']['admin']
MQTT_PASSWORD = config['mqtt']['password']
# 订阅消息主题--本机ID
MQTT_PUB_TOPIC = config['mqtt']['pub_topic']
# 推送消息主题--用户ID
MQTT_SUB_TOPIC = config['mqtt']['sub_topic']

"""串口配置"""
SERIAL_PORT = config['serial']['port']
SERIAL_BAUD = config['serial']['baudrate']
# 客户端设备配置
EQU_UUID = config['equ']['uuid']
EQU_NAME = config['equ']['name']
EQU_VERSION = config['equ']['version']

import serial
import serial.tools.list_ports

class SerialError(Exception):
    pass

class Ser(object):
    def __init__(self, que_msg, que_publish):
        self.que_msg = que_msg
        self.que_publish = que_publish
        # self.Find()
        self.ser_init()

    def ser_init(self):
        '''初始化串口'''
        self.serial = serial.Serial()
        self.serial.timeout = 0.5# make sure that the alive event can be checked from time to time
        self.serial_alive = False
        self.serial.port = SERIAL_PORT
        self.serial.baudrate = SERIAL_BAUD
        self.serial.bytesize = 8
        self.serial.parity   = 'N'
        self.serial.stopbits = 1
        # self.serial.dtr      = True
        # self.serial.rts      = True
        self.recv = ''
        self.thread_recv = None
        self.thread_write = None

    def find(self):
        """查询串口"""
        p = None
        if platform.system() == 'Windows':
            port_list = list(serial.tools.list_ports.comports())
            p = [port[0]+':'+port[1] for port in port_list]
        if platform.system() == 'Linux':
            port_list = list(serial.tools.list_ports.comports())
            p = [port[0] for port in port_list]
        print(p)

    def open(self):
        """打开串口"""
        if self.serial.port and self.serial.baudrate:
            try:
                self.serial.open()
            except serial.SerialException as e:
                print("Open Seial Error ：%s" % e)
                logging.debug("Open Seial Error ：%s"%e)
            else:
                self.serial_alive = True
                self.thread_recv = Thread(target=self.recv_hander)
                self.thread_recv.setDaemon(True)
                self.thread_recv.start()
                self.thread_write = Thread(target=self.write_hander)
                self.thread_write.setDaemon(True)
                self.thread_write.start()
                print("Open Serial: %s / %s"%(self.serial.port, self.serial.baudrate))
                logging.debug("Open Serial: %s / %s"%(self.serial.port, self.serial.baudrate))
        else:
            print("Serial Error : port or baudrate is not setting!!!")
            logging.debug("Serial Error : port or baudrate is not setting!!!")

    def recv_hander(self):
        """串口接收"""
        while self.serial_alive:
            try:
                n = self.serial.inWaiting()
                if n:
                    b = self.serial.read(n)
                    self.que_publish.put(b.decode('utf-8'))
                    print("Serial Recv : %s"%b.decode('utf-8').replace('\r\n','\\r\\n'))
                    logging.info("Serial Recv : %s"%b.decode('utf-8').replace('\r\n','\\r\\n'))
            except Exception as e:
                self.serial_alive = False
                logging.warning(e)

    def write_hander(self):
        """接收到que队列中的消息后通过串口发送出去"""
        while self.serial_alive:
            try:
                msg = self.que_msg.get()
                if msg:
                    if self.serial_alive:
                        if self.serial.isOpen():
                            self.serial.write(msg.encode('utf-8'))
                            print("Serial Send : %s \r\n" % msg)
                            logging.info("Serial Send : %s \r\n" % msg)
                    else:
                        print("serial is not open!!!")
            except Exception as e:
                self.serial_alive = False
                logging.warning(e)

    def write(self, msg):
        """串口写入msg,写入需要将字符串转换成bytes"""
        if self.serial_alive:
            if self.serial.isOpen():
                self.serial.write(msg.encode('utf-8'))
        else:
            print("serial is not open!!!")

    def close(self):
        """关闭串口"""
        self.serial_alive = False
        try:
            self.serial.close()
            self.thread_recv = None
            logging.info("Serial Close Successful!!!")
        except (SerialError) as e:
            logging.info("Serial Close Error : %s"%e)

class MqttClientHander(object):
    def __init__(self, que_msg, que_publish):
        self.que_msg = que_msg
        self.que_publish = que_publish
        # 创建推送线程
        self.thread_publish = Thread(target=self.on_publish_serial)
        self.thread_publish.setDaemon(True)
        self.thread_publish.start()
        # mqtt配置
        self.pub_topic = str(MQTT_PUB_TOPIC)
        self.sub_topic = str(MQTT_SUB_TOPIC)
        self.host = MQTT_HOST
        self.port = MQTT_PORT
        self.admin = MQTT_ADMIN
        self.password = MQTT_PASSWORD
        # 终端设备信息
        self.equ_uuid = EQU_UUID
        self.equ_name = EQU_NAME
        self.equ_version = EQU_VERSION

    def client_loop(self):
        self.client_id = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
        self.client = mqtt.Client(self.client_id)  # ClientId不能重复，所以使用当前时间
        self.client.username_pw_set(self.admin, self.password)  # 必须设置，否则会返回「Connected with result code 4」
        self.client.on_connect = self.on_connect
        self.client.on_message = self.on_message
        self.client.connect(self.host, self.port, 60)
        self.client.loop_forever()

    def on_connect(self, client, userdata, flags, rc):
        """客户端订阅消息"""
        print("Connected with result code " + str(rc))
        logging.info("Connected with result code " + str(rc))
        topic_list = self.sub_topic.split(';')
        topic_list = [(topic_list[i], i) for i in range(0, len(topic_list)) if topic_list[i]]
        print("Subscribe List：", topic_list)
        """subscribe([("my/topic", 0), ("another/topic", 2)])"""
        self.client.subscribe(topic_list)
        self.on_publish_more("%s:online"%self.equ_uuid)

    def on_publish(self, topic, payload, qos):
        """客户端推送消息"""
        self.client.publish(topic, payload, qos)

    def on_publish_more(self, payload):
        """客户端推送多条消息"""
        if self.pub_topic:
            topic_list = str(self.pub_topic).split(';')
            for topic in topic_list:
                if topic:
                    self.client.publish(topic, payload)
                    print('MQTT Publish : [%s] %s' % (topic, payload))
                    logging.info('MQTT Publish : [%s] %s' % (topic, payload))

    def on_publish_serial(self):
        """线程推送串口消息"""
        while True:
            data = self.que_publish.get()
            if data:
                self.on_publish_more(data)

    def on_message(self, client, userdata, msg):
        """客户端接收消息"""
        message = str(msg.payload.decode("utf-8"))
        print("MQTT Recv : [{topic}]: {msg}".format(topic=msg.topic, msg=message))
        logging.info("MQTT Recv: [{topic}] : {msg}".format(topic=msg.topic, msg=message))
        # 设置推送主题、开始添加设备
        # with open(config_file, 'w') as c:
        #     topic = message[len("XY+ST="):]
        #     config.set('mqtt', 'pub_topic', topic)
        #     self.pub_topic = topic
        #     config.write(c)
        if message.startswith("XY+ST="):
            topic = message[len("XY+ST="):]
            self.pub_topic = topic
            inf = {
                    'uuid': self.equ_uuid,
                    'name': self.equ_name,
                    'version': self.equ_version
                }
            self.on_publish_more("INF:"+json.dumps(inf))
        # 获取终端设备ID
        # if message=="XY+GID?":
        #     self.on_publish_more("XY+GID=ff6be6ebb6")
        # 获取设备信息
        # if message=="XY+INF?":
        #     inf = {
        #         'name': self.equ_name,
        #         'version': self.equ_version
        #     }
        #     self.on_publish_more("INF:"+json.dumps(inf))

        # 打开阀门
        if message == "XY+TAP=1":
            self.on_publish_more("TAP:1")
        # 打开阀门
        if message == "XY+TAP=0":
            self.on_publish_more("TAP:0")
        # self.que_msg.put(message)

    def _ensureNewline(self, s):
        "Makes sure a string ends in a newline."
        if s and s[-1] != '\n':
            s += '\r\n'
        return s

if __name__ == '__main__':
    que_msg = Queue()
    que_publish = Queue()

    ser = Ser(que_msg, que_publish)
    ser.open()

    mt = MqttClientHander(que_msg, que_publish)
    mt.client_loop()
